package st.learnopengl.apps.advance

import glm_.glm
import glm_.mat4x4.Mat4
import glm_.vec3.Vec3
import glm_.vec4.Vec4
import gln.BufferTarget
import gln.Usage
import gln.VertexAttrType
import gln.draw.glDrawElements
import gln.glClearColor
import gln.glf.VertexAttribute
import gln.identifiers.GlBuffer
import gln.identifiers.GlProgram
import gln.vertexArray.GlVertexArray
import st.learnopengl.common.App
import st.learnopengl.common.GLUT
import org.lwjgl.opengl.GL11C.*
import kotlin.math.abs

class BoneAnimation(width: Int, height: Int, title: String) : App(width, height, title) {

    private var program: GlProgram? = null
    private var vao: GlVertexArray? = null
    private var vbo: GlBuffer? = null
    private var ibo: GlBuffer? = null
    private lateinit var aPos: VertexAttribute
    private lateinit var aBoneIndex: VertexAttribute
    private lateinit var aWeight: VertexAttribute
    private var bones_location: Int = -1
    private var rotate_value: Float = 0.0f
    private val rotate_delta: Float = 0.2f
    private var rotate_direct: Int = 1
    override fun onInit() {
        program = GLUT.createProgram("shaders/boneanim/basic_vs.glsl"
            , "shaders/boneanim/basic_fs.glsl")

        bones_location = program!!.getUniformLocation("bones")
        vao = GlVertexArray.gen()
        vao!!.bound {
            vbo = GlBuffer.gen()
            vbo!!.bound(BufferTarget.ARRAY) {
                data(box_vertex, Usage.STATIC_DRAW)
            }

            ibo = GlBuffer.gen()
            ibo!!.bound(BufferTarget.ELEMENT_ARRAY) {
                data(box_index)
            }
        }

        aPos = VertexAttribute(0, 3, VertexAttrType.FLOAT,false, 7 * 4, 0)
        aBoneIndex = VertexAttribute(1, 2, VertexAttrType.FLOAT, false, 7 * 4, 3 * 4)
        aWeight = VertexAttribute(2, 2, VertexAttrType.FLOAT, false, 7 * 4, 5 * 4)

        glClearColor(Vec4(0.3, 0.4, 0.5, 1.0))
        glLineWidth(3.0f)
    }

    override fun loop() {
        glClear(GL_COLOR_BUFFER_BIT)

        program!!.use {
            val bones = arrayOf(Mat4(), Mat4())

            if (abs(rotate_value) >= 20.0f) rotate_direct *= -1
            rotate_value += rotate_delta * rotate_direct

            bones[0] = bones[0].rotate(glm.radians(rotate_value), Vec3(0, 0, 1))
            bones[1] = bones[1].rotate(glm.radians(-rotate_value), Vec3(0, 0, 1))

            this.program.programUniform(bones_location, bones)

            vao!!.bound {
                vbo!!.bound(BufferTarget.ARRAY) {
                    ibo!!.bound(BufferTarget.ELEMENT_ARRAY) {
                        aPos.enable()
                        aPos.pointer()

                        aBoneIndex.enable()
                        aBoneIndex.pointer()

                        aWeight.enable()
                        aWeight.pointer()

                        glDrawElements(12)
                    }
                }
            }
        }

    }


    companion object {
        val box_vertex = floatArrayOf(
            // position           bone index   weights
            -0.25f, 0.0f, 0.0f,   0.0f, 1.0f, 0.5f, 0.5f,       // index 0
            -0.25f, -0.75f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,      // index 1
            0.25f, -0.75f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,       // index 2
            0.25f, 0.0f, 0.0f,   0.0f, 1.0f, 0.5f, 0.5f,        // index 3
            -0.25f, 0.75f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,       // index 4
            0.25f, 0.75f, 0.0f,  0.0f, 1.0f, 0.0f, 1.0f       // index 5
        )

        val box_index = intArrayOf(
            0, 1, 2,
            0, 2, 3,
            4, 0, 3,
            4, 3, 5
        )

        @JvmStatic
        fun main(args: Array<String>) {
            BoneAnimation(1280, 720, "Bone Animation").run()
        }
    }
}